package com.xiaoniu.forum.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xiaoniu.forum.common.constant.ForumConstant;
import com.xiaoniu.forum.common.constant.GlobalMessageType;
import com.xiaoniu.forum.common.event.ForumEvent;
import com.xiaoniu.forum.common.utils.SpringUtils;
import com.xiaoniu.forum.dao.MemberDao;
import com.xiaoniu.forum.entity.MemberEntity;
import com.xiaoniu.forum.entity.UnreadEntity;
import com.xiaoniu.forum.entity.dto.QueryDTO;
import com.xiaoniu.forum.entity.vo.Dialogue;
import com.xiaoniu.forum.entity.vo.GlobalMessageVo;
import com.xiaoniu.forum.service.WebSocketMessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaoniu.forum.common.utils.PageUtils;
import com.xiaoniu.forum.common.utils.Query;

import com.xiaoniu.forum.dao.MemberMessageDao;
import com.xiaoniu.forum.entity.MemberMessageEntity;
import com.xiaoniu.forum.service.MemberMessageService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.socket.WebSocketMessage;

import javax.websocket.Session;


@Service("memberMessageService")
public class MemberMessageServiceImpl extends ServiceImpl<MemberMessageDao, MemberMessageEntity> implements MemberMessageService {
    private static final String CREATE_TIME_COLUMN = "create_time";
    private static final String SENDER_ID_COLUMN = "sender_id";
    private static final String RECIPIENT_ID_COLUMN = "recipient_id";
    private static final String TYPE_COLUMN = "type";
    private static final String ID_COLUMN = "id";

    @Autowired
    private MemberDao memberDao;

    //保存所有用户 全局websocekt session的集合，主要用于推送通知
    @Autowired
    @Qualifier("globalSessionMap")
    private Map<String, Session> globalSessionMap;

    @Autowired
    private WebSocketMessageService socketMessageService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<MemberMessageEntity> page = this.page(
                new Query<MemberMessageEntity>().getPage(params),
                new QueryWrapper<MemberMessageEntity>()
        );
        return new PageUtils(page);
    }


    /**
     *   将事件对象转换为消息保存到数据库中
     */
    @Override
    public MemberMessageEntity sendEvent(ForumEvent forumEvent) {
        MemberMessageEntity message = new MemberMessageEntity();
        message.setSenderId(forumEvent.getTriggerId());
        message.setRecipientId(forumEvent.getTargetId());
        message.setIsRead(ForumConstant.FALSE);
        message.setContent(JSON.toJSONString(forumEvent.getData()));
        message.setType(forumEvent.getAction()+"");
        message.setCreateTime(new Date());
        this.save(message);
        pushNotice(message);
        return message;
    }

    @Override
    public boolean sendMessage(MemberMessageEntity messageEntity) {
        messageEntity.setIsRead(ForumConstant.FALSE);
        messageEntity.setCreateTime(new Date());
        this.save(messageEntity);
        pushNotice(messageEntity);
        return true;
    }

    /**
     *  通过websocket给该条消息相关的用户推送通知
     */
    private void pushNotice(MemberMessageEntity messageEntity){
        GlobalMessageVo globalMessageVo = new GlobalMessageVo();
        globalMessageVo.setType(GlobalMessageType.NEW_MESSAGE);
        try {
            socketMessageService.sendMessageToMember(messageEntity.getSenderId(),JSON.toJSONString(globalMessageVo),globalSessionMap);
            socketMessageService.sendMessageToMember(messageEntity.getRecipientId(),JSON.toJSONString(globalMessageVo),globalSessionMap);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



    /**
     *   查询用户的对话列表
     *   每个对话需要的数据：消息发送者信息(ID、头像、昵称等等)，未读记录数，最新的一条记录
     *   思路：
     *      一、查询所需的数据
     *          1、查询出与当前用户相关的一批消息
     *          2、查询出当前用户多条未读记录数 发送人id ：记录数
     *          3、查询出用户的信息(ID、头像、昵称)
     *
     *      二、将查询出来的数据，封装成Dialogue对象
     *          1、将用户相关的消息和 他的多条未读记录数
     *          2、封装用户信息
     *
     */
    @Override
    public List<Dialogue> dialogueList(String memberId) {
        List<Dialogue> dialogues = new ArrayList<>();
        List<MemberMessageEntity> messages = aboutMeLastBatchMessage(memberId);
        List<UnreadEntity> unreadEntitys = baseMapper.groupQueryUnreadCountBySenderId(memberId);
        for (MemberMessageEntity message:messages){
            Dialogue dialogue = new Dialogue();
            dialogue.setLastMessage(message);
            packUnreadCount(dialogue,message,unreadEntitys);
            packUserInfo(dialogue,message,memberId);
            dialogues.add(dialogue);
        }
        return dialogues;
    }

    /**
     *    查询与用户相关的最后一条消息
     */
    @Override
    public MemberMessageEntity getLastMessage(String memberId) {
        QueryWrapper<MemberMessageEntity>queryWrapper = new QueryWrapper<MemberMessageEntity>();
        queryWrapper
                .eq(SENDER_ID_COLUMN,memberId)
                .or()
                .eq(RECIPIENT_ID_COLUMN,memberId);
        Page<MemberMessageEntity> page = new Page<>(1,1);
        page.addOrder(OrderItem.desc(CREATE_TIME_COLUMN));
        IPage<MemberMessageEntity> resultPage = this.page(page, queryWrapper);
        if (resultPage.getRecords().size() > 0){
            return resultPage.getRecords().get(0);
        }
        return null;
    }

    /**
     *查询用户与某个用户的聊天记录，消息按时间倒序分页获取 (根据消息类型查询)
     * 用户查询完消息列表以后，需要将另一位用户发给他的消息全部已读
     */
    @Override
    public PageUtils historyList(String currentUserId, String anotherUserId, QueryDTO queryDTO) {
        QueryWrapper<MemberMessageEntity>queryWrapper = new QueryWrapper<>();
        if (queryDTO.getMessageType()!=null){
            queryWrapper.eq(TYPE_COLUMN,queryDTO.getMessageType());//只查询某种类型的消息
        }
        buildUserRelatedQueryWrapper(queryWrapper,currentUserId,anotherUserId);
        if (queryDTO.getMessageType()!=null){
            queryWrapper.eq(TYPE_COLUMN,queryDTO.getMessageType());//只查询某种类型的消息
        }
        Page<MemberMessageEntity> page = new Page<>(queryDTO.getCurrentPage(),queryDTO.getSize());
        page.addOrder(OrderItem.desc(CREATE_TIME_COLUMN));
        IPage<MemberMessageEntity> resultPage = this.page(page, queryWrapper);

        //将另一位用户发给当前用户的消息，全部已读
        Map<String, String> map = new HashMap<>();
        map.put("senderId",anotherUserId);
        map.put("recipientId",currentUserId);
        baseMapper.batchRead(map);
        return new PageUtils(resultPage);
    }

    /**
     *   获取两个用户之间最早的历史消息
     */
    @Override
    public MemberMessageEntity getEarliestHistoryMessage(String id1, String id2) {
        QueryWrapper<MemberMessageEntity> queryWrapper = new QueryWrapper<>();
        buildUserRelatedQueryWrapper(queryWrapper,id1, id2);
        queryWrapper.orderByAsc(CREATE_TIME_COLUMN);
        Page<MemberMessageEntity> page = new Page<>(1,1);
        page.addOrder(OrderItem.asc(CREATE_TIME_COLUMN));
        return getOneEntity(page,queryWrapper);
    }

    /**
     *   根据"消息类型"分页查询历史记录 ,根据消息创建时间倒序查询
     */
    @Override
    public PageUtils historyListByType(String id, Integer type, Integer currentPage, Integer size) {
        QueryWrapper<MemberMessageEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(TYPE_COLUMN,type);
        queryWrapper.eq(RECIPIENT_ID_COLUMN,id);
        Page<MemberMessageEntity> page = new Page<>(currentPage,size);
        page.addOrder(OrderItem.desc(CREATE_TIME_COLUMN));
        IPage<MemberMessageEntity> resultPage = this.page(page, queryWrapper);

        //拉取完消息记录，将它们设置为已读
        MemberMessageEntity message = new MemberMessageEntity();
        message.setIsRead(ForumConstant.TRUE);
        this.update(message,queryWrapper);
        return new PageUtils(resultPage);
    }


    /**
     * 查询与用户相关的多组消息，每组拿最新的一条消息
     *  与用户相关：用户是消息发送者或者用户是消息接收者的消息，都是与用户相关的
     *
     *  查询思路：
     *
     *      1、查询消息发送者是当前用户的最新一批消息
     *      2、查询消息接收者是当前用户的最新一批消息
     *      3、给这两组去重，当消息发送者 + 消息接收者是重叠时，拿创建时间最晚的一条消息
     */

    private List<MemberMessageEntity> aboutMeLastBatchMessage(String memberId){
        List<MemberMessageEntity> mySendMessages = this.baseMapper.mySendLastBatchMessage(memberId);
        List<MemberMessageEntity> myReceiveMessages = this.baseMapper.myReceiveLastBatchMessage(memberId);
        //消息去重,去重后，所有消息都在noOverlapList中了
        for (MemberMessageEntity mySendMessage:mySendMessages){
            removeOverlap(mySendMessage,myReceiveMessages);
        }
        System.out.println(myReceiveMessages.size());
        System.out.println(myReceiveMessages.size());
        System.out.println(myReceiveMessages.size());
        return myReceiveMessages;
    }

    /**
     *   消息去重， 根据 消息发送者 + 消息接收者 去重
     *   消息去重,如果我发送的消息中的接收者id和我接收的消息中的发送者id是一致的，那么就是重叠了
     *   如果两条消息重叠，取消息创建时间较晚的那条消息放入集合
     *   如果该条消息和集合中的任何消息都不重叠，则直接添加到集合中
     */
    private void removeOverlap(MemberMessageEntity mySendMeesage,List<MemberMessageEntity> myReceiveMessages){
        ListIterator<MemberMessageEntity> myReceives = myReceiveMessages.listIterator();
        while (myReceives.hasNext()){
            MemberMessageEntity myReceiveMessage = myReceives.next();
            if (isOverlap(mySendMeesage,myReceiveMessage)){
                //如果重叠，判断时间大小
                if (mySendMeesage.getCreateTime().getTime() - myReceiveMessage.getCreateTime().getTime() >= 0) {
                    myReceives.remove();
                    myReceives.add(mySendMeesage);
                }
                return;
            }
        }
        myReceiveMessages.add(mySendMeesage);
    }


    /**
     * 判断两条消息是否重叠
     * 两条消息的发送人和接收人都一致，那么肯定重叠
     * 消息1的发送人等于消息2的接收人，并且消息1的接收人等于消息2的发送人，那么也重叠
     */
    private boolean isOverlap(MemberMessageEntity message1,MemberMessageEntity message2){
        if (message1.getSenderId().equals(message2.getSenderId()) && message1.getRecipientId().equals(message2.getRecipientId())){
            return true;
        }
        if (message1.getSenderId().equals(message2.getRecipientId()) && message1.getRecipientId().equals(message2.getSenderId())){
            return true;
        }
        return false;
    }

    /**
     *  封装对话的对应的未读记录数
     */
    private void packUnreadCount(Dialogue dialogue,MemberMessageEntity message,List<UnreadEntity> unreadEntitys){
        /* 封装对话的未读记录数（这一块逻辑有点绕，总之就是 消息的发送人和未读记录的发送人是同一个，
                                    那么对话最后一条消息就是发给当前用户的消息，否则就是用户发给其他人的，
                                    既然对话的最后一条消息都是用户发的，那么自然没有未读的记录了）
        */
        for (UnreadEntity unread:unreadEntitys){
            //如果消息的发送人，和未读记录的发送人相同，那么对话的未读记录数就是这个值
            if (message.getSenderId().equals(unread.getSenderId())){
                dialogue.setUnreadCount(unread.getUnreadCount());
                return;
            }else {//否则对话的未读记录数就是0
                dialogue.setUnreadCount(0);
            }
        }
    }

    /**
     *  封装对话的用户信息
     */
    private void packUserInfo(Dialogue dialogue,MemberMessageEntity message,String currentMemberId){
        Map<String,Object> userInfo = new HashMap<>();
        String memberId = null;
        //消息的发送人如果是当前用户，那么就查接收人的信息，否则就查发件人的信息
        if (message.getSenderId().equals(currentMemberId)){
            memberId = message.getRecipientId();
        }else {
            memberId = message.getSenderId();
        }
        //FIXME  这里如果要查询的用户是系统预定的ID，就不查询，暂时用该办法，以后如果有更好的方案需要修改
        if(Long.parseLong(memberId) <= 10){
            userInfo.put("id",memberId);
            dialogue.setUserInfo(userInfo);
            return;
        }
        MemberEntity member = memberDao.selectById(memberId);
        if (member != null) {
            userInfo.put("id", member.getId());
            userInfo.put("nickname", member.getNickname());
            userInfo.put("avatar", member.getAvatar());
            dialogue.setUserInfo(userInfo);
        }
    }


    /**
     * 构建查询两个用户对话消息的查询条件
     */
    private QueryWrapper<MemberMessageEntity> buildUserRelatedQueryWrapper(QueryWrapper<MemberMessageEntity> queryWrapper,String userId1,String userId2){
        queryWrapper
                .eq(SENDER_ID_COLUMN,userId1)
                .eq(RECIPIENT_ID_COLUMN,userId2)
                .or()
                .eq(RECIPIENT_ID_COLUMN,userId1)
                .eq(SENDER_ID_COLUMN,userId2);
        return queryWrapper;
    }

    /**
     *
     */
    private MemberMessageEntity getOneEntity(Page<MemberMessageEntity> page,QueryWrapper<MemberMessageEntity> queryWrapper){
        IPage<MemberMessageEntity> resultPage = this.page(page, queryWrapper);
        if (resultPage.getRecords().size() > 0){
            return resultPage.getRecords().get(0);
        }
        return null;
    }

}